﻿//
// This file is part of AVSReflector project
// Copyright (C) 2011 Yuan Pengfei
//

using System;
using System.Collections.Generic;
using System.Text;

namespace AVSReflector.Core
{
    public enum MacroBlockType
    {
        // P
        P_Skip,
        P_16x16,
        P_16x8,
        P_8x16,
        P_8x8,
        I_8x8,
        // B
        B_Skip,
        B_Direct_16x16,
        B_Fwd_16x16,
        B_Bck_16x16,
        B_Sym_16x16,
        B_Fwd_Fwd_16x8,
        B_Fwd_Fwd_8x16,
        B_Bck_Bck_16x8,
        B_Bck_Bck_8x16,
        B_Fwd_Bck_16x8,
        B_Fwd_Bck_8x16,
        B_Bck_Fwd_16x8,
        B_Bck_Fwd_8x16,
        B_Fwd_Sym_16x8,
        B_Fwd_Sym_8x16,
        B_Bck_Sym_16x8,
        B_Bck_Sym_8x16,
        B_Sym_Fwd_16x8,
        B_Sym_Fwd_8x16,
        B_Sym_Bck_16x8,
        B_Sym_Bck_8x16,
        B_Sym_Sym_16x8,
        B_Sym_Sym_8x16,
        B_8x8
        // I_8x8 again
    }

    public enum MacroBlockPartType
    {
        SB_Direct_8x8,
        SB_Fwd_8x8,
        SB_Bck_8x8,
        SB_Sym_8x8
    }

    public class MacroBlock : INode
    {
        public uint mb_index;
        public MacroBlockType type;
        public MacroBlockPartType[] part_type;
        public uint cbp;

        public uint mb_type;
        public uint[] pred_mode_flag;
        public uint[] intra_luma_pred_mode;
        public uint intra_chroma_pred_mode;
        public uint intra_chroma_pred_mode_422;
        public uint[] mb_reference_index;
        public int[] mv_diff_x;
        public int[] mv_diff_y;

        public BlockPredMode[] inter_luma_pred_mode;
        public int[] ref_index;
        public int[] diff_x;
        public int[] diff_y;

        public bool intra;

        public Block[] blocks;

        public string[] GetInfo()
        {
            List<string> lines = new List<string>();
            lines.Add("mb_index = " + mb_index);
            lines.Add("mb_type = " + NameOfType(type));
            if (type != MacroBlockType.P_Skip && type != MacroBlockType.B_Skip)
            {
                lines.Add("mb_cbp = " + cbp);
                if (type == MacroBlockType.B_8x8)
                {
                    for (int i = 0; i < 4; ++i)
                        lines.Add("mb_part_type[" + i + "] = " + NameOfPartType(part_type[i]));
                }
            }
            lines.Add("");
            lines.Add("\t=== Blocks of MacroBlock ===");
            lines.Add("");
            for (int i = 0; i < blocks.Length; ++i)
                lines.AddRange(blocks[i].GetInfo());
            return lines.ToArray();
        }

        public static MacroBlock Parse(BitStream s, bool skip = false)
        {
            MacroBlock ret = new MacroBlock();
            ret.mb_index = Parameters.MbIndex;
            if (Parameters.MbIndex >= Context.currentSequence.mb_height * Context.currentSequence.mb_width / 2)
                Parameters.DistanceIndex = (int)Context.currentPicture.picture_distance * 2 + 1;
            else
                Parameters.DistanceIndex = (int)Context.currentPicture.picture_distance * 2;
            if (skip)
            {
                if (Context.currentPicture.picture_type == 2)
                    ret.type = MacroBlockType.B_Skip;
                else
                    ret.type = MacroBlockType.P_Skip;
                Parameters.MbType = ret.type;
                Parameters.MbCBP = 0;
                Parameters.MbCBP422 = 0;
                s = null;
            }
            else
            {
                if (Context.currentPicture.picture_type != 0 && Context.currentPicture.picture_type != 4
                    || Context.currentPicture.picture_structure == 0
                    && Parameters.MbIndex >= Context.currentSequence.mb_height * Context.currentSequence.mb_width / 2)
                    ret.mb_type = Decoder.ExpGolomb(s);
                Parameters.MbType = ret.Type();
                ret.type = Parameters.MbType;
                #region B_8x8
                if (Parameters.MbType == MacroBlockType.B_8x8)
                {
                    Parameters.MvNum = 0;
                    Parameters.MbPartType = new MacroBlockPartType[4];
                    Parameters.MvPartNum = new uint[4];
                    for (int i = 0; i < 4; ++i)
                    {
                        uint idx = s.Read(2);
                        switch (idx)
                        {
                            case 0:
                                Parameters.MvPartNum[i] = 0;
                                Parameters.MbPartType[i] = MacroBlockPartType.SB_Direct_8x8;
                                break;
                            case 1:
                                Parameters.MvPartNum[i] = 1;
                                Parameters.MbPartType[i] = MacroBlockPartType.SB_Fwd_8x8;
                                break;
                            case 2:
                                Parameters.MvPartNum[i] = 1;
                                Parameters.MbPartType[i] = MacroBlockPartType.SB_Bck_8x8;
                                break;
                            default: // case 4
                                Parameters.MvPartNum[i] = 1;
                                Parameters.MbPartType[i] = MacroBlockPartType.SB_Sym_8x8;
                                break;
                        }
                        Parameters.MvNum += Parameters.MvPartNum[i];
                        ret.part_type = Parameters.MbPartType;
                    }
                }
                #endregion
                #region I_8x8
                if (Parameters.MbType == MacroBlockType.I_8x8)
                {
                    ret.pred_mode_flag = new uint[4];
                    ret.intra_luma_pred_mode = new uint[4];
                    for (int i = 0; i < 4; ++i)
                    {
                        ret.pred_mode_flag[i] = s.Read(1);
                        if (ret.pred_mode_flag[i] == 0)
                            ret.intra_luma_pred_mode[i] = s.Read(2);
                    }
                    if (Context.currentSequence.chroma_format != 0)
                        ret.intra_chroma_pred_mode = Decoder.ExpGolomb(s);
                    if (Context.currentSequence.chroma_format == 2)
                        ret.intra_chroma_pred_mode_422 = Decoder.ExpGolomb(s);
                }
                #endregion
                if (Context.currentPicture.picture_reference_flag == 0
                    && (Context.currentPicture.picture_type == 1
                    || Context.currentPicture.picture_type == 2 && Context.currentPicture.picture_structure == 0))
                {
                    ret.mb_reference_index = new uint[Parameters.MvNum];
                    for (int i = 0; i < Parameters.MvNum; ++i)
                    {
                        // TODO: Judge according to new standard
                        if (Context.currentPicture.picture_type == 1
                            && Context.currentPicture.picture_structure == 0)
                            ret.mb_reference_index[i] = s.Read(2);
                        else
                            ret.mb_reference_index[i] = s.Read(1);
                    }
                }
                ret.mv_diff_x = new int[Parameters.MvNum];
                ret.mv_diff_y = new int[Parameters.MvNum];
                for (int i = 0; i < Parameters.MvNum; ++i)
                {
                    ret.mv_diff_x[i] = Decoder.SignedExpGolomb(s);
                    ret.mv_diff_y[i] = Decoder.SignedExpGolomb(s);
                }
                ret.intra = (Parameters.MbType == MacroBlockType.I_8x8  // Guessed
                    || Context.currentPicture.picture_type == 0
                    && (Context.currentPicture.picture_structure == 1
                    || Parameters.MbIndex < Context.currentSequence.mb_height * Context.currentSequence.mb_width / 2));
                if (!(Context.currentPicture.picture_type == 2 && Parameters.MbTypeIndex >= 24
                    || Context.currentPicture.picture_type != 2 && Parameters.MbTypeIndex >= 5))
                {
                    uint codenum = Decoder.ExpGolomb(s);
                    Parameters.MbCBP = Decoder.CBPLookup(codenum, ret.intra);
                }
                else
                    Parameters.MbCBP = Decoder.CBPLookup(Parameters.CBPCodeNum, ret.intra);
                ret.cbp = Parameters.MbCBP;
                if (Context.currentSequence.chroma_format == 2)
                {
                    uint codenum = Decoder.ExpGolomb(s);
                    Parameters.MbCBP422 = Decoder.CPB422Lookup(codenum);
                }
                int delta = 0;
                if (Parameters.FixedQP == 0 && (Parameters.MbCBP > 0 ||
                    Context.currentSequence.chroma_format == 2 && Parameters.MbCBP422 > 0))
                    delta = Decoder.SignedExpGolomb(s);
                Parameters.CurrentQP = Parameters.PreviousQP + delta;
            }

            int x = (int)(Parameters.MbIndex % Context.currentSequence.mb_width);
            int y = (int)(Parameters.MbIndex / Context.currentSequence.mb_width);
            Context.currentPicture.BlockMatrix[y, x] = ret;

            // Parse blocks
            ret.GetInterLumaPredModes();
            ret.GetReferenceIndexesAndMVDiffs();
            MacroBlock bMB = Context.currentMB;
            Context.currentMB = ret;
            List<Block> l = new List<Block>();
            for (int i = 0; i < 4; ++i)
            {
                #region Decide intra luma pred mode
                if (Parameters.MbType == MacroBlockType.I_8x8)
                {
                    int a = -1, b = -1;
                    if (i % 2 == 0)
                    {
                        MacroBlock mb = ret.GetLeft();
                        if (mb != null && mb.type == MacroBlockType.I_8x8)
                            a = mb.blocks[i + 1].intra_pred_mode;
                    }
                    else
                        a = l[i - 1].intra_pred_mode;
                    if (i < 2)
                    {
                        MacroBlock mb = ret.GetUp();
                        if (mb != null && mb.type == MacroBlockType.I_8x8)
                            b = mb.blocks[i + 2].intra_pred_mode;
                    }
                    else
                        b = l[i - 2].intra_pred_mode;
                    int pred = Math.Min(a, b);
                    if (pred < 0)
                        pred = 2;
                    if (ret.pred_mode_flag[i] == 1)
                        Parameters.IntraLumaPredMode = pred;
                    else
                    {
                        Parameters.IntraLumaPredMode = (int)ret.intra_luma_pred_mode[i];
                        if (ret.intra_luma_pred_mode[i] >= pred)
                            Parameters.IntraLumaPredMode++;
                    }
                }
                #endregion
                Parameters.InterBlockPredMode = ret.inter_luma_pred_mode[i];
                Parameters.ReferenceIndex = ret.ref_index[i];
                Parameters.MVDiffX = ret.diff_x[i];
                Parameters.MVDiffY = ret.diff_y[i];

                ret.blocks = l.ToArray();
                l.Add(Block.Parse(s, i));
            }
            ret.blocks = l.ToArray();
            if (Context.currentSequence.chroma_format == 1)
            {
                Parameters.IntraChromaPredMode = (int)ret.intra_chroma_pred_mode;
                for (int i = 4; i < 6; ++i)
                    l.Add(Block.Parse(s, i));
            }
            if (Context.currentSequence.chroma_format == 2)
            {
                Parameters.IntraChromaPredMode = (int)ret.intra_chroma_pred_mode;
                for (int i = 4; i < 6; ++i)
                    l.Add(Block.Parse(s, i));
                Parameters.IntraChromaPredMode = (int)ret.intra_chroma_pred_mode_422;
                for (int i = 6; i < 8; ++i)
                    l.Add(Block.Parse(s, i));
            }

            if (!skip)
            {
                Context.currentMB = bMB;
                Parameters.PreviousQP = Parameters.CurrentQP;
            }

            ret.blocks = l.ToArray();
            return ret;
        }

        MacroBlockType Type()
        {
            // I & P picture type
            Parameters.MbTypeIndex = 0;
            if (Context.currentPicture.picture_type <= 1)
            {
                if (Context.currentPicture.picture_type == 0 && (Context.currentPicture.picture_structure == 1
                    || Context.currentPicture.picture_structure == 0
                    && Parameters.MbIndex < Context.currentSequence.mb_width * Context.currentSequence.mb_height / 2))
                {
                    Parameters.MvNum = 0;
                    return MacroBlockType.I_8x8;
                }
                uint index;
                if (Context.currentPicture.skip_mode_flag == 1)
                    index = mb_type + 1;
                else
                    index = mb_type;
                if (index >= 5)
                {
                    Parameters.CBPCodeNum = index - 5;
                    index = 5;
                }
                Parameters.MbTypeIndex = index;
                switch (index)
                {
                    case 0:
                        Parameters.MvNum = 0;
                        return MacroBlockType.P_Skip;
                    case 1:
                        Parameters.MvNum = 1;
                        return MacroBlockType.P_16x16;
                    case 2:
                        Parameters.MvNum = 2;
                        return MacroBlockType.P_16x8;
                    case 3:
                        Parameters.MvNum = 2;
                        return MacroBlockType.P_8x16;
                    case 4:
                        Parameters.MvNum = 4;
                        return MacroBlockType.P_8x8;
                    default: // case 5
                        Parameters.MvNum = 0;
                        return MacroBlockType.I_8x8;
                }
            }
            // B picture type
            uint idx;
            if (Context.currentPicture.skip_mode_flag == 1)
                idx = mb_type + 1;
            else
                idx = mb_type;
            if (idx >= 24)
            {
                Parameters.CBPCodeNum = idx - 24;
                idx = 24;
            }
            Parameters.MbTypeIndex = idx;
            switch (idx)
            {
                case 0:
                    Parameters.MvNum = 0;
                    return MacroBlockType.B_Skip;
                case 1:
                    Parameters.MvNum = 0;
                    return MacroBlockType.B_Direct_16x16;
                case 2:
                    Parameters.MvNum = 1;
                    return MacroBlockType.B_Fwd_16x16;
                case 3:
                    Parameters.MvNum = 1;
                    return MacroBlockType.B_Bck_16x16;
                case 4:
                    Parameters.MvNum = 1;
                    return MacroBlockType.B_Sym_16x16;
                case 5:
                    Parameters.MvNum = 2;
                    return MacroBlockType.B_Fwd_Fwd_16x8;
                case 6:
                    Parameters.MvNum = 2;
                    return MacroBlockType.B_Fwd_Fwd_8x16;
                case 7:
                    Parameters.MvNum = 2;
                    return MacroBlockType.B_Bck_Bck_16x8;
                case 8:
                    Parameters.MvNum = 2;
                    return MacroBlockType.B_Bck_Bck_8x16;
                case 9:
                    Parameters.MvNum = 2;
                    return MacroBlockType.B_Fwd_Bck_16x8;
                case 10:
                    Parameters.MvNum = 2;
                    return MacroBlockType.B_Fwd_Bck_8x16;
                case 11:
                    Parameters.MvNum = 2;
                    return MacroBlockType.B_Bck_Fwd_16x8;
                case 12:
                    Parameters.MvNum = 2;
                    return MacroBlockType.B_Bck_Fwd_8x16;
                case 13:
                    Parameters.MvNum = 2;
                    return MacroBlockType.B_Fwd_Sym_16x8;
                case 14:
                    Parameters.MvNum = 2;
                    return MacroBlockType.B_Fwd_Sym_8x16;
                case 15:
                    Parameters.MvNum = 2;
                    return MacroBlockType.B_Bck_Sym_16x8;
                case 16:
                    Parameters.MvNum = 2;
                    return MacroBlockType.B_Bck_Sym_8x16;
                case 17:
                    Parameters.MvNum = 2;
                    return MacroBlockType.B_Sym_Fwd_16x8;
                case 18:
                    Parameters.MvNum = 2;
                    return MacroBlockType.B_Sym_Fwd_8x16;
                case 19:
                    Parameters.MvNum = 2;
                    return MacroBlockType.B_Sym_Bck_16x8;
                case 20:
                    Parameters.MvNum = 2;
                    return MacroBlockType.B_Sym_Bck_8x16;
                case 21:
                    Parameters.MvNum = 2;
                    return MacroBlockType.B_Sym_Sym_16x8;
                case 22:
                    Parameters.MvNum = 2;
                    return MacroBlockType.B_Sym_Sym_8x16;
                case 23:
                    Parameters.MvNum = 0;
                    return MacroBlockType.B_8x8;
                default: // case 24
                    Parameters.MvNum = 0;
                    return MacroBlockType.I_8x8;
            }
        }

        public static string NameOfType(MacroBlockType t)
        {
            string ret = null;
            switch (t)
            {
                case MacroBlockType.P_Skip:
                    ret = "P_Skip"; break;
                case MacroBlockType.P_16x16:
                    ret = "P_16x16"; break;
                case MacroBlockType.P_16x8:
                    ret = "P_16x8"; break;
                case MacroBlockType.P_8x16:
                    ret = "P_8x16"; break;
                case MacroBlockType.P_8x8:
                    ret = "P_8x8"; break;
                case MacroBlockType.I_8x8:
                    ret = "I_8x8"; break;
                case MacroBlockType.B_Skip:
                    ret = "B_Skip"; break;
                case MacroBlockType.B_Direct_16x16:
                    ret = "B_Direct_16x16"; break;
                case MacroBlockType.B_Fwd_16x16:
                    ret = "B_Fwd_16x16"; break;
                case MacroBlockType.B_Bck_16x16:
                    ret = "B_Bck_16x16"; break;
                case MacroBlockType.B_Sym_16x16:
                    ret = "B_Sym_16x16"; break;
                case MacroBlockType.B_Fwd_Fwd_16x8:
                    ret = "B_Fwd_Fwd_16x8"; break;
                case MacroBlockType.B_Fwd_Fwd_8x16:
                    ret = "B_Fwd_Fwd_8x16"; break;
                case MacroBlockType.B_Bck_Bck_16x8:
                    ret = "B_Bck_Bck_16x8"; break;
                case MacroBlockType.B_Bck_Bck_8x16:
                    ret = "B_Bck_Bck_8x16"; break;
                case MacroBlockType.B_Fwd_Bck_16x8:
                    ret = "B_Fwd_Bck_16x8"; break;
                case MacroBlockType.B_Fwd_Bck_8x16:
                    ret = "B_Fwd_Bck_8x16"; break;
                case MacroBlockType.B_Bck_Fwd_16x8:
                    ret = "B_Bck_Fwd_16x8"; break;
                case MacroBlockType.B_Bck_Fwd_8x16:
                    ret = "B_Bck_Fwd_8x16"; break;
                case MacroBlockType.B_Fwd_Sym_16x8:
                    ret = "B_Fwd_Sym_16x8"; break;
                case MacroBlockType.B_Fwd_Sym_8x16:
                    ret = "B_Fwd_Sym_8x16"; break;
                case MacroBlockType.B_Bck_Sym_16x8:
                    ret = "B_Bck_Sym_16x8"; break;
                case MacroBlockType.B_Bck_Sym_8x16:
                    ret = "B_Bck_Sym_8x16"; break;
                case MacroBlockType.B_Sym_Fwd_16x8:
                    ret = "B_Sym_Fwd_16x8"; break;
                case MacroBlockType.B_Sym_Fwd_8x16:
                    ret = "B_Sym_Fwd_8x16"; break;
                case MacroBlockType.B_Sym_Bck_16x8:
                    ret = "B_Sym_Bck_16x8"; break;
                case MacroBlockType.B_Sym_Bck_8x16:
                    ret = "B_Sym_Bck_8x16"; break;
                case MacroBlockType.B_Sym_Sym_16x8:
                    ret = "B_Sym_Sym_16x8"; break;
                case MacroBlockType.B_Sym_Sym_8x16:
                    ret = "B_Sym_Sym_8x16"; break;
                case MacroBlockType.B_8x8:
                    ret = "B_8x8"; break;
                default:
                    ret = ""; break;
            }
            return ret;
        }

        static string NameOfPartType(MacroBlockPartType t)
        {
            string ret = null;
            if (t == MacroBlockPartType.SB_Direct_8x8)
                ret = "SB_Direct_8x8";
            else if (t == MacroBlockPartType.SB_Bck_8x8)
                ret = "SB_Bck_8x8";
            else if (t == MacroBlockPartType.SB_Fwd_8x8)
                ret = "SB_Fwd_8x8";
            else if (t == MacroBlockPartType.SB_Sym_8x8)
                ret = "SB_Sym_8x8";
            return ret;
        }

        MacroBlock GetLeft()
        {
            MacroBlock ret = null;
            uint start = Context.currentSlice.slice_vertical_position * Context.currentSequence.mb_width;
            if (mb_index % Context.currentSequence.mb_width >= 1)
                ret = Context.currentSlice.blocks[mb_index - start - 1];
            return ret;
        }

        MacroBlock GetUp()
        {
            MacroBlock ret = null;
            uint start = Context.currentSlice.slice_vertical_position * Context.currentSequence.mb_width;
            int i = (int)(mb_index - start - Context.currentSequence.mb_width);
            if (i >= 0)
                ret = Context.currentSlice.blocks[i];
            return ret;
        }

        void GetInterLumaPredModes()
        {
            inter_luma_pred_mode = new BlockPredMode[4];
            switch(type)
            {
                case MacroBlockType.P_Skip:
                case MacroBlockType.B_Skip:
                    for (int i = 0; i < 4; ++i)
                        inter_luma_pred_mode[i] = BlockPredMode.Skip;
                    break;
                case MacroBlockType.P_16x16:
                case MacroBlockType.P_16x8:
                case MacroBlockType.P_8x16:
                case MacroBlockType.P_8x8:
                case MacroBlockType.B_Fwd_16x16:
                case MacroBlockType.B_Fwd_Fwd_16x8:
                case MacroBlockType.B_Fwd_Fwd_8x16:
                    for (int i = 0; i < 4; ++i)
                        inter_luma_pred_mode[i] = BlockPredMode.Forward;
                    break;
                case MacroBlockType.B_Direct_16x16:
                    for (int i = 0; i < 4; ++i)
                        inter_luma_pred_mode[i] = BlockPredMode.Direct;
                    break;
                case MacroBlockType.B_Bck_16x16:
                case MacroBlockType.B_Bck_Bck_16x8:
                case MacroBlockType.B_Bck_Bck_8x16:
                    for (int i = 0; i < 4; ++i)
                        inter_luma_pred_mode[i] = BlockPredMode.Backward;
                    break;
                case MacroBlockType.B_Sym_16x16:
                case MacroBlockType.B_Sym_Sym_16x8:
                case MacroBlockType.B_Sym_Sym_8x16:
                    for (int i = 0; i < 4; ++i)
                        inter_luma_pred_mode[i] = BlockPredMode.Symmetric;
                    break;
                case MacroBlockType.B_Fwd_Bck_16x8:
                    inter_luma_pred_mode[0] = BlockPredMode.Forward;
                    inter_luma_pred_mode[1] = BlockPredMode.Forward;
                    inter_luma_pred_mode[2] = BlockPredMode.Backward;
                    inter_luma_pred_mode[3] = BlockPredMode.Backward;
                    break;
                case MacroBlockType.B_Fwd_Bck_8x16:
                    inter_luma_pred_mode[0] = BlockPredMode.Forward;
                    inter_luma_pred_mode[1] = BlockPredMode.Backward;
                    inter_luma_pred_mode[2] = BlockPredMode.Forward;
                    inter_luma_pred_mode[3] = BlockPredMode.Backward;
                    break;
                case MacroBlockType.B_Bck_Fwd_16x8:
                    inter_luma_pred_mode[0] = BlockPredMode.Backward;
                    inter_luma_pred_mode[1] = BlockPredMode.Backward;
                    inter_luma_pred_mode[2] = BlockPredMode.Forward;
                    inter_luma_pred_mode[3] = BlockPredMode.Forward;
                    break;
                case MacroBlockType.B_Bck_Fwd_8x16:
                    inter_luma_pred_mode[0] = BlockPredMode.Backward;
                    inter_luma_pred_mode[1] = BlockPredMode.Forward;
                    inter_luma_pred_mode[2] = BlockPredMode.Backward;
                    inter_luma_pred_mode[3] = BlockPredMode.Forward;
                    break;
                case MacroBlockType.B_Fwd_Sym_16x8:
                    inter_luma_pred_mode[0] = BlockPredMode.Forward;
                    inter_luma_pred_mode[1] = BlockPredMode.Forward;
                    inter_luma_pred_mode[2] = BlockPredMode.Symmetric;
                    inter_luma_pred_mode[3] = BlockPredMode.Symmetric;
                    break;
                case MacroBlockType.B_Fwd_Sym_8x16:
                    inter_luma_pred_mode[0] = BlockPredMode.Forward;
                    inter_luma_pred_mode[1] = BlockPredMode.Symmetric;
                    inter_luma_pred_mode[2] = BlockPredMode.Forward;
                    inter_luma_pred_mode[3] = BlockPredMode.Symmetric;
                    break;
                case MacroBlockType.B_Bck_Sym_16x8:
                    inter_luma_pred_mode[0] = BlockPredMode.Backward;
                    inter_luma_pred_mode[1] = BlockPredMode.Backward;
                    inter_luma_pred_mode[2] = BlockPredMode.Symmetric;
                    inter_luma_pred_mode[3] = BlockPredMode.Symmetric;
                    break;
                case MacroBlockType.B_Bck_Sym_8x16:
                    inter_luma_pred_mode[0] = BlockPredMode.Backward;
                    inter_luma_pred_mode[1] = BlockPredMode.Symmetric;
                    inter_luma_pred_mode[2] = BlockPredMode.Backward;
                    inter_luma_pred_mode[3] = BlockPredMode.Symmetric;
                    break;
                case MacroBlockType.B_Sym_Fwd_16x8:
                    inter_luma_pred_mode[0] = BlockPredMode.Symmetric;
                    inter_luma_pred_mode[1] = BlockPredMode.Symmetric;
                    inter_luma_pred_mode[2] = BlockPredMode.Forward;
                    inter_luma_pred_mode[3] = BlockPredMode.Forward;
                    break;
                case MacroBlockType.B_Sym_Fwd_8x16:
                    inter_luma_pred_mode[0] = BlockPredMode.Symmetric;
                    inter_luma_pred_mode[1] = BlockPredMode.Forward;
                    inter_luma_pred_mode[2] = BlockPredMode.Symmetric;
                    inter_luma_pred_mode[3] = BlockPredMode.Forward;
                    break;
                case MacroBlockType.B_Sym_Bck_16x8:
                    inter_luma_pred_mode[0] = BlockPredMode.Symmetric;
                    inter_luma_pred_mode[1] = BlockPredMode.Symmetric;
                    inter_luma_pred_mode[2] = BlockPredMode.Backward;
                    inter_luma_pred_mode[3] = BlockPredMode.Backward;
                    break;
                case MacroBlockType.B_Sym_Bck_8x16:
                    inter_luma_pred_mode[0] = BlockPredMode.Symmetric;
                    inter_luma_pred_mode[1] = BlockPredMode.Backward;
                    inter_luma_pred_mode[2] = BlockPredMode.Symmetric;
                    inter_luma_pred_mode[3] = BlockPredMode.Backward;
                    break;
                case MacroBlockType.B_8x8:
                    for(int i=0; i<4; ++i)
                    {
                        switch(part_type[i])
                        {
                            case MacroBlockPartType.SB_Bck_8x8:
                                inter_luma_pred_mode[i] = BlockPredMode.Backward;
                                break;
                            case MacroBlockPartType.SB_Direct_8x8:
                                inter_luma_pred_mode[i] = BlockPredMode.Direct;
                                break;
                            case MacroBlockPartType.SB_Fwd_8x8:
                                inter_luma_pred_mode[i] = BlockPredMode.Forward;
                                break;
                            case MacroBlockPartType.SB_Sym_8x8:
                                inter_luma_pred_mode[i] = BlockPredMode.Symmetric;
                                break;
                        }
                    }
                    break;
            }
        }

        void GetReferenceIndexesAndMVDiffs()
        {
            ref_index = new int[4];
            diff_x = new int[4];
            diff_y = new int[4];
            if (mb_reference_index == null)
                mb_reference_index = new uint[4];
            int k = 0;
            switch (type)
            {
                case MacroBlockType.P_Skip:
                case MacroBlockType.B_Skip:
                case MacroBlockType.I_8x8:
                case MacroBlockType.B_Direct_16x16:
                    break;
                case MacroBlockType.P_16x16:
                case MacroBlockType.B_Fwd_16x16:
                case MacroBlockType.B_Bck_16x16:
                case MacroBlockType.B_Sym_16x16:
                    for (int i = 0; i < 4; ++i)
                    {
                        ref_index[i] = (int)mb_reference_index[0];
                        diff_x[i] = mv_diff_x[0];
                        diff_y[i] = mv_diff_y[0];
                    }
                    break;
                case MacroBlockType.B_Bck_Fwd_16x8:
                case MacroBlockType.B_Bck_Sym_16x8:
                    ref_index[0] = (int)mb_reference_index[1];
                    ref_index[1] = (int)mb_reference_index[1];
                    ref_index[2] = (int)mb_reference_index[0];
                    ref_index[3] = (int)mb_reference_index[0];
                    diff_x[0] = mv_diff_x[1];
                    diff_x[1] = mv_diff_x[1];
                    diff_x[2] = mv_diff_x[0];
                    diff_x[3] = mv_diff_x[0];
                    diff_y[0] = mv_diff_y[1];
                    diff_y[1] = mv_diff_y[1];
                    diff_y[2] = mv_diff_y[0];
                    diff_y[3] = mv_diff_y[0];
                    break;
                case MacroBlockType.P_16x8:
                case MacroBlockType.B_Fwd_Fwd_16x8:
                case MacroBlockType.B_Bck_Bck_16x8:
                case MacroBlockType.B_Fwd_Bck_16x8:
                case MacroBlockType.B_Fwd_Sym_16x8:
                case MacroBlockType.B_Sym_Fwd_16x8:
                case MacroBlockType.B_Sym_Bck_16x8:
                case MacroBlockType.B_Sym_Sym_16x8:
                    ref_index[0] = (int)mb_reference_index[0];
                    ref_index[1] = (int)mb_reference_index[0];
                    ref_index[2] = (int)mb_reference_index[1];
                    ref_index[3] = (int)mb_reference_index[1];
                    diff_x[0] = mv_diff_x[0];
                    diff_x[1] = mv_diff_x[0];
                    diff_x[2] = mv_diff_x[1];
                    diff_x[3] = mv_diff_x[1];
                    diff_y[0] = mv_diff_y[0];
                    diff_y[1] = mv_diff_y[0];
                    diff_y[2] = mv_diff_y[1];
                    diff_y[3] = mv_diff_y[1];
                    break;
                case MacroBlockType.B_Bck_Fwd_8x16:
                case MacroBlockType.B_Bck_Sym_8x16:
                    ref_index[0] = (int)mb_reference_index[1];
                    ref_index[1] = (int)mb_reference_index[0];
                    ref_index[2] = (int)mb_reference_index[1];
                    ref_index[3] = (int)mb_reference_index[0];
                    diff_x[0] = mv_diff_x[1];
                    diff_x[1] = mv_diff_x[0];
                    diff_x[2] = mv_diff_x[1];
                    diff_x[3] = mv_diff_x[0];
                    diff_y[0] = mv_diff_y[1];
                    diff_y[1] = mv_diff_y[0];
                    diff_y[2] = mv_diff_y[1];
                    diff_y[3] = mv_diff_y[0];
                    break;
                case MacroBlockType.P_8x16:
                case MacroBlockType.B_Fwd_Fwd_8x16:
                case MacroBlockType.B_Bck_Bck_8x16:
                case MacroBlockType.B_Fwd_Bck_8x16:
                case MacroBlockType.B_Fwd_Sym_8x16:
                case MacroBlockType.B_Sym_Fwd_8x16:
                case MacroBlockType.B_Sym_Bck_8x16:
                case MacroBlockType.B_Sym_Sym_8x16:
                    ref_index[0] = (int)mb_reference_index[0];
                    ref_index[1] = (int)mb_reference_index[1];
                    ref_index[2] = (int)mb_reference_index[0];
                    ref_index[3] = (int)mb_reference_index[1];
                    diff_x[0] = mv_diff_x[0];
                    diff_x[1] = mv_diff_x[1];
                    diff_x[2] = mv_diff_x[0];
                    diff_x[3] = mv_diff_x[1];
                    diff_y[0] = mv_diff_y[0];
                    diff_y[1] = mv_diff_y[1];
                    diff_y[2] = mv_diff_y[0];
                    diff_y[3] = mv_diff_y[1];
                    break;
                case MacroBlockType.P_8x8:
                    for (int i = 0; i < 4; ++i)
                    {
                        ref_index[i] = (int)mb_reference_index[i];
                        diff_x[i] = mv_diff_x[i];
                        diff_y[i] = mv_diff_y[i];
                    }
                    break;
                case MacroBlockType.B_8x8:
                    for (int i = 0; i < 4; ++i)
                        if (part_type[i] == MacroBlockPartType.SB_Fwd_8x8
                            || part_type[i] == MacroBlockPartType.SB_Sym_8x8)
                        {
                            ref_index[i] = (int)mb_reference_index[k];
                            diff_x[i] = mv_diff_x[k];
                            diff_y[i] = mv_diff_y[k];
                            k++;
                        }
                    for (int i = 0; i < 4; ++i)
                        if (part_type[i] == MacroBlockPartType.SB_Bck_8x8)
                        {
                            ref_index[i] = (int)mb_reference_index[k];
                            diff_x[i] = mv_diff_x[k];
                            diff_y[i] = mv_diff_y[k];
                            k++;
                        }
                    break;
            }
        }
    }
}
